<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMenuApi } from '#/api/system/menu';
import type { SystemRoleApi } from '#/api/system/role';

import { computed, defineProps, nextTick, onMounted, ref, watch } from 'vue';

import { message } from 'ant-design-vue';

import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getMenuList } from '#/api/system/menu';
import { getOAuth2ClientList } from '#/api/system/oauth2/client';
import { assignRoleMenu, getRoleMenuList } from '#/api/system/permission';

import { useAssignMenuFormGridColumns } from '../data';
// 选择的角色
const props = defineProps({
  role: {
    type: Object as () => SystemRoleApi.Role,
    default: null,
  },
});

const allMenus = ref<SystemMenuApi.Menu[]>([]); // 所有菜单数据

const checkedMenuIds = ref<number[]>([]); // 已选中的菜单 ID 列表（含按钮 ID）

const isExpanded = ref(false); // 是否展开所有树节点

const isLinkedMode = ref(true); // 是否开启“节点关联”模式（勾选父自动勾选子）

const clientId = ref<string>(''); // 选择的应用ID
const clientOptions = ref<{ label: string; value: string }[]>([]); // 可以选择的应用列表

// 加载应用列表
async function loadClientOptions() {
  try {
    const list = await getOAuth2ClientList();
    clientOptions.value = list.map((item: any) => ({
      label: item.name,
      value: item.clientId,
    }));

    // 默认选中第一条
    if (clientOptions.value.length > 0) {
      clientId.value = clientOptions.value[0].value;
    }
  } catch {
    message.error('加载应用列表失败');
  }
}

// 页面加载时获取
onMounted(() => {
  loadClientOptions();
});

/** 工具函数：更新某个按钮 ID 是否选中 */
function updateCheckedMenuIds(id: number, checked: boolean) {
  const index = checkedMenuIds.value.indexOf(id);
  if (checked && index === -1) {
    checkedMenuIds.value.push(id);
  } else if (!checked && index !== -1) {
    checkedMenuIds.value.splice(index, 1);
  }
}

/** 获取某个菜单的所有子孙菜单（递归） */
function getAllDescendants(menuId: number, menus: SystemMenuApi.Menu[]) {
  const descendants: SystemMenuApi.Menu[] = [];

  function findChildren(id: number) {
    menus.forEach((item) => {
      if (item.parentId === id) {
        descendants.push(item);
        findChildren(item.id);
      }
    });
  }

  findChildren(menuId);
  return descendants;
}

/**
 * 找出所有的按钮级别的菜单，挂载到对应父菜单的 buttonMenus 属性上
 * @param menus 菜单列表
 * @returns 处理后的菜单列表，每个父菜单包含其按钮菜单的 buttonMenus 属性
 */
function attachButtonMenus(menus: SystemMenuApi.Menu[]): SystemMenuApi.Menu[] {
  const menuMap = new Map<number, SystemMenuApi.Menu>();

  // 给每个菜单添加空的按钮数组，并存入map
  menus.forEach((menu) => {
    menuMap.set(menu.id, menu);
    (menu as any).buttonMenus = [];
  });

  // 把所有按钮菜单(type === 3)挂载到对应的父菜单的buttonMenus数组里
  menus.forEach((menu) => {
    if (menu.type === 3 && menu.parentId) {
      const parent = menuMap.get(menu.parentId);
      if (parent) {
        (parent as any).buttonMenus.push(menu);
      }
    }
  });

  return menus;
}

// 点击“展开/收起”按钮
function toggleExpand() {
  isExpanded.value = !isExpanded.value;
  if (gridApi?.grid) {
    gridApi.grid.setAllTreeExpand(isExpanded.value);
  }
}

// 设置是否为“节点关联模式”
function setLinkedMode(val: boolean) {
  isLinkedMode.value = val;
  message.info(
    `切换成功,当前模式：${isLinkedMode.value ? '节点关联' : '节点独立'}`,
  );
}

/**
 * 根据菜单 ID 列表设置已勾选的菜单行（高亮显示 + 复选框选中）
 * @param menuList 所有菜单数据
 * @param menuIds 需要勾选的菜单 ID 列表
 */
function setCheckedRowsByMenuIds(
  menuList: SystemMenuApi.Menu[],
  menuIds: number[],
) {
  if (gridApi?.grid) {
    gridApi.grid.clearCheckboxRow(); // 清除之前的所有勾选项
    const recordsToCheck = menuList.filter((m) => menuIds.includes(m.id)); // 根据 ID 找出要勾选的菜单项
    gridApi.grid.setCheckboxRow(recordsToCheck, true); // 勾选对应的菜单行
  }
}

// 获取当前所有已选中的菜单 ID（按钮 + 菜单）
function getCheckedMenuIds(): number[] {
  const records = gridApi.grid?.getCheckboxRecords() || [];
  const menuIds = records.map((r) => r.id);
  return [...new Set([...checkedMenuIds.value, ...menuIds])];
}

// 保存权限菜单配置
async function handleSave() {
  const menuIds = getCheckedMenuIds();
  if (!props.role?.id) return;
  const hide = message.loading('正在保存权限...');
  try {
    await assignRoleMenu({
      roleId: props.role.id,
      menuIds,
    });
    hide();
    message.success('权限保存成功');
    // 刷新
    await gridApi.reload();
  } catch {
    hide();
    message.error('保存失败');
  }
}

// 勾选/取消勾选按钮权限时触发
function onButtonCheckboxChange(
  row: SystemMenuApi.Menu,
  buttons: SystemMenuApi.Menu[],
  btnId: number,
  checked: boolean,
) {
  updateCheckedMenuIds(btnId, checked);

  if (isLinkedMode.value) {
    // 如果按钮联动，则更新对应菜单行是否选中
    const allButtonIds = buttons.map((b) => b.id);
    const anyChecked = allButtonIds.some((id) =>
      checkedMenuIds.value.includes(id),
    );
    gridApi?.grid?.setCheckboxRow(row, anyChecked);
  }
}

// 初始化 vxe-grid 表格实例
const [Grid, gridApi] = useVbenVxeGrid({
  gridOptions: {
    id: 'assign-menu-table',
    columns: useAssignMenuFormGridColumns(
      checkedMenuIds,
      onButtonCheckboxChange,
      computed(() => props.role.editable), // 响应式
    ), // 表头列定义
    height: 'auto',
    proxyConfig: {
      ajax: {
        query: async () => {
          return loadRoleMenus(props.role);
        },
      },
    },
    pagerConfig: {
      enabled: false,
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    treeConfig: {
      transform: true,
      rowField: 'id',
      parentField: 'parentId',
      expandAll: false,
      accordion: false,
    },
    checkboxConfig: {
      highlight: true,
      checkMethod: () => props.role.editable,
    },
    onCheckboxChange: ({ row, checked }) => {
      updateCheckedMenuIds(row.id, checked);

      const buttons = row.buttonMenus || [];
      const descendants = getAllDescendants(row.id, allMenus.value);

      if (isLinkedMode.value) {
        // 联动：菜单选中 → 所有子菜单、按钮也选中
        buttons.forEach((btn: any) => updateCheckedMenuIds(btn.id, checked));
        descendants.forEach((menu) => {
          updateCheckedMenuIds(menu.id, checked);
          const btns = (menu as any).buttonMenus || [];
          btns.forEach((btn: any) => updateCheckedMenuIds(btn.id, checked));
        });
      }
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<SystemMenuApi.Menu>,
});

// 监听 role 值变化，重新加载数据
watch(
  () => props.role,
  async (role) => {
    if (!role?.id) return;
    await nextTick();
    await gridApi.reload();
  },
  { immediate: true },
);

// 监听 clientId 变化
watch(
  clientId,
  async (val) => {
    if (!props.role?.id) return;
    await nextTick();
    await gridApi.reload();
  },
  { immediate: true }, // 页面初始化也触发
);
/**
 * 加载角色菜单数据
 * @param role 选择的角色
 */
async function loadRoleMenus(role: SystemRoleApi.Role) {
  if (!role?.id) return [];

  const hideLoading = message.loading({
    content: '加载权限菜单...',
    key: 'role_menu_load',
  });

  try {
    const [menuList, roleMenuIds] = await Promise.all([
      getMenuList({ clientId: clientId.value }), // 获取所有菜单数据
      getRoleMenuList(role.id), // 获取角色已分配的菜单数据
    ]);

    // 把按钮级别的菜单挂载到对应的父菜单的 buttonMenus 属性上
    const processedMenus = attachButtonMenus(menuList);
    // 过滤掉按钮级别的菜单
    const filteredMenus = processedMenus.filter((item) => item.type !== 3);

    allMenus.value = filteredMenus;
    checkedMenuIds.value = roleMenuIds;

    await nextTick();
    // 设置已勾选的菜单行
    setCheckedRowsByMenuIds(filteredMenus, roleMenuIds);

    message.success({ content: '加载成功', key: 'role_menu_load' });

    return filteredMenus; // 返回数据数组
  } catch {
    message.error({ content: '加载失败', key: 'role_menu_load' });
    return [];
  } finally {
    hideLoading();
  }
}
</script>

<template>
  <Grid table-title="角色权限菜单">
    <template #toolbar-tools>
      <label style="margin-right: 4px">应用名称：</label>
      <a-select
        v-model:value="clientId"
        placeholder="请选择应用"
        style="width: 200px; margin-right: 12px"
        :options="clientOptions"
      />

      <TableAction
        :actions="[
          {
            label: isExpanded ? '收缩' : '展开',
            type: 'dashed',
            onClick: toggleExpand,
          },
          {
            label: '节点关联',
            type: isLinkedMode ? 'primary' : 'default',
            disabled: !props.role.editable || isLinkedMode, // 不可编辑 或 已经是节点关联模式
            onClick: () => setLinkedMode(true),
          },
          {
            label: '节点独立',
            type: !isLinkedMode ? 'primary' : 'default',
            disabled: !props.role.editable || !isLinkedMode, // 不可编辑 或 已经是节点独立模式
            onClick: () => setLinkedMode(false),
          },
          {
            label: '保存权限',
            type: 'primary',
            disabled: !props.role.editable,
            onClick: handleSave,
          },
        ]"
      />
    </template>
  </Grid>
</template>
